#include <types.h>
#include <Debug.h>
#include <CPU.h>
#include <string.h>

void TextPanic(const char *str);

char CPU::CPUBrand[13];
char CPU::Name[49];
CPUFamilyStepping CPU::FamilyStepping;
CPUBrandID CPU::BrandID;
CPUFeatures CPU::Features;

bool CPU::DisableInt() {
	dword flags = 0;
	asm volatile ("PUSHFL\n\
			POP	%%EAX"
                       :"=a" (flags));
	asm volatile ("CLI");
	return flags & (1 << 9);
}

void CPU::EnableInt() {
	asm volatile ("STI");
}

void CPU::Halt() {
	asm volatile ("HLT");
}

void CPU::Stop() {
	DisableInt();
	Halt();
}

dword CPU::ReadCR0() {
	dword ret;
	asm volatile ("MOVL	 %%CR0, %0"
			:"=r" (ret)
			:);
	return ret;
}

dword CPU::ReadCR1() {
	dword ret;
	asm volatile ("MOVL	 %%CR1, %0"
			:"=r" (ret)
			:);
	return ret;
}

dword CPU::ReadCR2() {
	dword ret;
	asm volatile ("MOVL	 %%CR2, %0"
			:"=r" (ret)
			:);
	return ret;
}

dword CPU::ReadCR3() {
	dword ret;
	asm volatile ("MOVL	 %%CR3, %0"
			:"=r" (ret)
			:);
	return ret;
}

dword CPU::ReadCR4() {
	dword ret;
	asm volatile ("MOVL	 %%CR4, %0"
			:"=r" (ret)
			:);
	return ret;
}

void CPU::WriteCR0(dword value) {
	asm volatile ("MOVL	%%EAX, %%CR0"
			:
			:"r" (value));
}

void CPU::WriteCR1(dword value) {
	asm volatile ("MOVL	%%EAX, %%CR1"
			:
			:"r" (value));
}

void CPU::WriteCR2(dword value) {
	asm volatile ("MOVL	%%EAX, %%CR2"
			:
			:"r" (value));
}

void CPU::WriteCR3(dword value) {
	asm volatile ("MOVL	%%EAX, %%CR3"
			:
			:"r" (value));
}

void CPU::WriteCR4(dword value) {
	asm volatile ("MOVL	%%EAX, %%CR4"
			:
			:"r" (value));
}

void CPU::CPUID(dword cmd, dword *a, dword *b, dword *c, dword *d) {
	asm volatile ("CPUID"
			:"=a" (*a), "=b" (*b), "=c" (*c), "=d" (*d)
			:"a" (cmd));
}

void CPU::ReadInfo() {
	dword eax, ebx, ecx, edx;
	dword MaxStandardEAX, MaxExtendedEAX;
	
	CPUID(0, &MaxStandardEAX, &ebx, &ecx, &edx);
	strncpy(CPUBrand, (char *) &ebx, 4);
	strncat(CPUBrand, (char *) &edx, 4);
	strncat(CPUBrand, (char *) &ecx, 4);
	
	if (MaxStandardEAX > 0) {
		CPUID(1, &eax, &ebx, &ecx, &edx);
		
		*((dword *) &FamilyStepping) = eax;
		*((dword *) &BrandID) = ebx;
		*((dword *) &Features) = edx;
		*((dword *) &Features + 1) = ecx;
		
		dputs("\nextFamily: ");
		dputdec(FamilyStepping.extFamily);
		dputs("\nextModel: ");
		dputdec(FamilyStepping.extModel);
		dputs("\nType: ");
		dputdec(FamilyStepping.type);
		dputs("\nFamily: ");
		dputdec(FamilyStepping.family);
		dputs("\nModel: ");
		dputdec(FamilyStepping.model);
		dputs("\nStepping: ");
		dputdec(FamilyStepping.stepping);

		dputs("\n\nBrandID: ");
		dputdec(BrandID.BrandID);
		dputs("\nCLFLUSH: ");
		dputdec(BrandID.CLFLUSH);
		dputs("\nCPUCount: ");
		dputdec(BrandID.CPUCount);
		dputs("\nAPICID: ");
		dputdec(BrandID.APICID);
		
		dputs("\n\nFPU: ");
		dputdec((dword) CPUHasFeature(FPU));
		dputs("\nSSE: ");
		dputdec((dword) CPUHasFeature(SSE));
		dputs("\nSSE2: ");
		dputdec((dword) CPUHasFeature(SSE2));
		dputs("\nSSE3: ");
		dputdec((dword) CPUHasFeature(SSE3));
		dputs("\nSSSE3: ");
		dputdec((dword) CPUHasFeature(SSSE3));
		dputs("\nSSE41: ");
		dputdec((dword) CPUHasFeature(SSE41));
		dputs("\nSSE42: ");
		dputdec((dword) CPUHasFeature(SSE42));
		dputs("\nMMX: ");
		dputdec((dword) CPUHasFeature(MMX));
		dputs("\nHTT: ");
		dputdec((dword) CPUHasFeature(HTT));
	}
	
	CPUID(0x80000000, &MaxExtendedEAX, &ebx, &ecx, &edx);
	if (MaxExtendedEAX > 1) {
		CPUID(0x80000002, (dword *) Name, (dword *) &Name[4], (dword *) &Name[8], (dword *) &Name[12]);
		CPUID(0x80000003, (dword *) &Name[16], (dword *) &Name[20], (dword *) &Name[24], (dword *) &Name[28]);
		CPUID(0x80000004, (dword *) &Name[32], (dword *) &Name[36], (dword *) &Name[40], (dword *) &Name[44]);
		Name[48] = 0;
		dputs("\nCPU Name: ");
		dputs(Name);
	}
}

